package cn.sprouting.tool;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.sprouting.enums.MajorIndustryIdentifierEnum;
import cn.sprouting.model.Bank;
import cn.sprouting.model.BankCard;
import cn.sprouting.service.BankService;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author ：LX
 * @ Date 创建时间： 2021/10/25. 13:18
 * 地点：长沙麓谷
 * 目的: 随机生成国内银行卡
 * 备注说明：
 *
 *       参考：效验是否为银行卡，用于验证：
 *       现行 16 位银联卡现行卡号开头 6 位是 622126～622925 之间的，7 到 15 位是银行自定义的，
 *       可能是发卡分行，发卡网点，发卡序号，第 16 位是校验码。
 *       16 位卡号校验位采用 Luhm 校验方法计算：
 *          1，将未带校验位的 15 位卡号从右依次编号 1 到 15，位于奇数位号上的数字乘以 2
 *          2，将奇位乘积的个十位全部相加，再加上所有偶数位上的数字
 *          3，将加法和加上校验位能被 10 整除。
 *
 *  获取银行卡属性信息查询
 *    中国银联 https://open.unionpay.com/tjweb/api/interface?apiSvcId=51&id=129
 *
 *  获取发卡行信息
 *      https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo=输入待校验的银行卡号&cardBinCheck=true
 *      入参：
 *        - cardNo：银行卡号
 *        - cardBinCheck：是否校验支付宝已绑此卡
 *        - _input_charset：编码格式
 *      返回：
 *        - bank：所属行简写
 *        - validated：有效性
 *        - cardType：卡类型 ，{@link cn.sprouting.enums.BankTypeEnum}
 *
 *  获取发卡行图标 https://apimg.alipay.com/combo.png?d=cashier&t=银行代码
 *      示例：https://apimg.alipay.com/combo.png?d=cashier&t=HBRCU
 *
 *  重要说明 ：
 *      注意，目前通过 luhm 计算出的校验码，并不适用于所有的银行卡，还是有部分的银行卡可能会不支持该验证，建议直接调用阿里的接口进行验
 *      证，{@link BankService#getCardDetail(String)}
 *      由于各银行的生成是有规则的，而程序无法完全收录每个银行的规则，故而，银行的发卡行中间部分很大可能都是异常的，当前的银行卡生成大部分还是有问题，并不是完全
 *      一个真实的银行卡数据
 *
 */
@Slf4j
public class RandomBankCardTool {

    private static BankService bankService = new BankService();


    /**
     * 生成一个银行卡信息
     * @return
     */
    public static BankCard getBank(){
        return getBank(1).get(0);
    }

    /**
     * 批量生成大量的银行卡信息
     * 注意：未做重复性校验
     *
     * @param num 生成的数量
     * @return
     */
    public static List<BankCard> getBank(int num){
        if (num < 1){
            num = 1;
        }

        List<Bank> all = BankService.all();
        List<BankCard> list = new ArrayList<>();
        for (int i = 0; i < num; i ++){
            list.add(build(all));
        }

        return list;
    }

    /**
     * 批量生成指定银行的银行卡信息
     * 注意：未做重复性校验
     *
     * @param bankNameList 指定银行名称，比如 中国银行,为null则查询全部
     * @param num 生成的数量，少于1则会自动设置为1
     * @return
     */
    public static List<BankCard> getBank(List<String> bankNameList, int num){
        List<Bank> all = BankService.all();
        if (bankNameList != null && bankNameList.size() > 0){
            all = all.stream().filter(obj -> bankNameList.contains(obj.getIssuer())).collect(Collectors.toList());
        }
        if (CollUtil.isEmpty(all)){
            log.error("[银行卡] 未找到对应的银行卡信息，无法生成");
            return null;
        }

        if (num < 1){
            num = 1;
        }
        List<BankCard> list = new ArrayList<>();
        for (int i = 0; i < num; i ++){
            list.add(build(all));
        }

        return list;
    }

    /**
     * 生成指定的银行的银行卡信息
     * @param bankNameList 指定银行名称，比如 中国银行
     * @return
     */
    public static BankCard getAppoint(List<String> bankNameList){
        List<BankCard> list = getBank(bankNameList, 1);
        if (list == null){
            return null;
        }
        return list.get(0);
    }

    /**
     * 生成指定的银行的银行卡信息
     * @param bankName 指定银行名称，比如 中国银行
     * @return
     */
    public static BankCard getBank(String bankName){
        if (StrUtil.isBlank(bankName)){
            log.error("[银行卡] 请指定银行卡发卡行名称");
            return null;
        }

        List<String> bankNameList = new ArrayList<>();
        bankNameList.add(bankName);
        List<BankCard> list = getBank(bankNameList, 1);
        if (list == null){
            return null;
        }
        return list.get(0);
    }


    /**
     * 随机生成一个银行卡号码
     *
     * @param bankList 用于随机的银行信息
     * @return 生成一个银行卡实体
     */
    private static BankCard build(List<Bank> bankList){
        Bank bank = RandomUtil.randomEle(bankList);
        String code = buildCode(bank);

        BankCard bankCard = new BankCard();
        bankCard.setCard(code);
        bankCard.setIssuer(bank.getIssuer());
        bankCard.setName(bank.getName());
        bankCard.setIin(bank.getBin());

        // 类型太多，暂时不确定
        bankCard.setType(null);
        bankCard.setTypeName(bank.getType());

        int mii = Integer.parseInt(code.substring(0, 1));
        bankCard.setMii(mii);
        MajorIndustryIdentifierEnum anEnum = MajorIndustryIdentifierEnum.getCode(mii);
        if (anEnum != null){
            bankCard.setMiiName(anEnum.getExplain());
        }

        if (bankService.checkBankCard(code)){
            bankCard.setCheckLuhm(true);
        } else {
            bankCard.setCheckLuhm(false);
        }

        return bankCard;
    }

    /**
     * 生成银行卡号
     * @param bank 银行卡
     * @return
     */
    private static String buildCode(Bank bank){

        StringBuilder code = new StringBuilder();
        // 长度 - 已使用的Bin长度 - 校验码
        for (int i = 0; i < bank.getLength() - bank.getBin().length() - 1; i ++){
            code.append(RandomUtil.randomInt(10));
        }

        String backCode = bank.getBin() + code;
        char[] chars = backCode.toCharArray();

        // 生成校验码
        int luhmSum = bankService.getLuhmSum(chars);
        return backCode + luhmSum;
    }


}
